package com.s.core.daily_import;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.s.core.daily_import.remote.Remote;
import com.s.core.daily_import.remote.vo.RemoteVo;
import com.s.core.daily_import.vo.Add;
import com.s.core.daily_import.vo.ImportResult;
import com.s.core.daily_import.vo.Rename;
import com.s.core.persist.PersistService;
import com.s.core.root.Basic;
import com.s.core.root.Ad;
import com.s.core.root.D;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author zhangjiangnan
 */
@Service
@Slf4j
public class ImportService implements InitializingBean {
    private static final int[] IMPORT_POS_N = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
            30, 40, 50, 60, 80, 100, 120};

    @Resource
    private PersistService persistService;

    @Override
    public void afterPropertiesSet() {
        if (com.s.S.IS_JAR && !PersistService.DEV_FOR_USE_DB) {
            final String[] nextTradeDate = {Remote.nextTradeDate(Ad.me().getDateList().get(0))};
            ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
            executor.scheduleAtFixedRate(() -> {
                if ((Utils.time()).compareTo(nextTradeDate[0] + " 15:05:00") > 0) {
                    log.info("start import {}", nextTradeDate[0]);

                    ImportResult result = process(Utils.tranDate(nextTradeDate[0], "yyyy-MM-dd", "yyyyMMdd"));
                    String date = Ad.me().getDateList().get(0);
                    persistService.saveCore(date);

                    persistService.saveDb(result);
                    log.info("insert db done,{},{}", result.addD.size(), Ad.me().getCodes().size());
                    persistService.dump(date);

                    nextTradeDate[0] = Remote.nextTradeDate(date);
                    log.info("end import,nextTradeDate {}", nextTradeDate[0]);
                }
            }, 0, 5, TimeUnit.SECONDS);
        }
    }

    @SuppressWarnings("unchecked")
    private ImportResult process(String date) {
        ImportResult result = new ImportResult(date);
        List<RemoteVo> voList = Remote.dailyData();
        List<String> dbCodes = Ad.me().getCodes();
        log.info("remote done,start check");
        //包含停牌和未上市
        List<RemoteVo> noCloseList = voList.stream().filter(vo -> vo.close == 0 || vo.vol == 0).collect(Collectors.toList());
        //包含未停牌和新上市
        List<RemoteVo> haveCloseList = voList.stream().filter(vo -> vo.close > 0 && vo.vol > 0).collect(Collectors.toList());
        result.adds = ((List<String>) CollectionUtils.subtract(RemoteVo.toCodes(haveCloseList), dbCodes)).
                stream().map(code -> new Add(code, RemoteVo.getByCode(voList, code).getName())).
                collect(Collectors.toList());
        result.removeCodes = (List<String>) CollectionUtils.subtract(dbCodes, RemoteVo.toCodes(voList));
        result.tpCodes = (List<String>) CollectionUtils.intersection(RemoteVo.toCodes(noCloseList), dbCodes);
        result.unMarket = (List<String>) CollectionUtils.subtract(RemoteVo.toCodes(noCloseList), result.tpCodes);
        result.cqCodes = haveCloseList.stream().filter(RemoteVo::isCq).map(RemoteVo::getCode).collect(Collectors.toList());
        result.renames = Lists.newArrayList();
        for (Basic basic : Ad.me().getBasicList()) {
            RemoteVo vo = RemoteVo.getByCode(voList, basic.getCode());
            if (vo != null && !vo.nameEquals(basic.getName())) {
                result.renames.add(new Rename(vo.getCode(), basic.getName(), vo.getName()));
            }
        }
        result.addD = Lists.newArrayList();
        for (RemoteVo vo : voList) {
            if (result.unMarket.contains(vo.code)) {
                continue;
            }
            D d = new D(vo.code, date);
            result.addD.add(d);
            if (!result.tpCodes.contains(vo.code)) {
                d.setClose(Utils.fen2yuan(vo.close));
                d.setVol(String.valueOf(vo.vol));
                d.setPreClose(Utils.fen2yuan(vo.preClose));
                d.setHigh(Utils.fen2yuan(vo.high));
            }
        }
        log.info("check done,result:{}", result);
        process(result);
        return result;
    }

    private void process(ImportResult result) {
        result.adds.forEach(add -> Ad.me().addBasic(add.code, add.name));
        result.removeCodes.forEach(code -> Ad.me().delBasic(code));
        result.renames.forEach(rename -> Ad.me().renameBasic(rename.code, rename.getNewName()));
        result.addD.forEach(d -> Ad.me().addDaily(d));
        Ad.me().removeDate(null);
        Ad.me().parse1();

        Map<String, Map<Integer, Integer>> codePosNSortIndexMap =
                getCodePosNSortIndex(result.date,
                        posN -> getSortList(result.date, posN, (o1, o2) -> Double.compare(o2.zf(posN, true), o1.zf(posN, true))));
        result.addD.forEach(d -> {
            String code = d.code;
            Map<Integer, Integer> np = codePosNSortIndexMap.get(code);
            for (int n : IMPORT_POS_N) {
                d.setPosN(n, np.get(n));
            }
        });
        Ad.me().parse2();
    }

    private List<D> getSortList(String date, int offset, Comparator<D> c) {
        return Ad.me().getDateDailyListMap().get(date).stream().filter(d -> {
            D bDaily = d.bDaily(offset);
            return !d.isTp() && bDaily != null && !bDaily.isTp();
        }).sorted(c).collect(Collectors.toList());
    }

    /**
     * 计算date日所有个股的所有posN的值
     */
    private Map<String, Map<Integer, Integer>> getCodePosNSortIndex(String date, Function<Integer, List<D>> sortListFunction) {
        //code,posN,sortIndex
        Map<String, Map<Integer, Integer>> codePosNSortIndexMap = Maps.newHashMap();
        for (int posN : IMPORT_POS_N) {
            List<D> sortList = sortListFunction.apply(posN);
            for (String code : Ad.me().getDateCodeDailyMap().get(date).keySet()) {
                if (!codePosNSortIndexMap.containsKey(code)) {
                    codePosNSortIndexMap.put(code, Maps.newHashMap());
                }
                int sortIndex = sortList.indexOf(Ad.me().getDaily(date, code));
                if (sortIndex != -1) {
                    codePosNSortIndexMap.get(code).put(posN, sortIndex + 1);
                } else {
                    codePosNSortIndexMap.get(code).put(posN, 0);
                }
            }
            //相同涨跌幅的POS也相同
            Map<Double, List<D>> zfDailyMap = Ad.me().getDateDailyListMap().get(date).stream().
                    filter(sortList::contains).collect(Collectors.groupingBy(d -> d.zf(posN, true)));
            for (Map.Entry<Double, List<D>> entry : zfDailyMap.entrySet()) {
                int min = entry.getValue().stream().map(d -> codePosNSortIndexMap.get(d.code).get(posN)).min(Integer::compareTo).get();
                int max = entry.getValue().stream().map(d -> codePosNSortIndexMap.get(d.code).get(posN)).max(Integer::compareTo).get();
                int newSortIndex = (min + max) / 2;
                entry.getValue().forEach(d -> codePosNSortIndexMap.get(d.code).put(posN, newSortIndex));
            }
        }
        return codePosNSortIndexMap;
    }
}